package com.fyz.aaronfjava.common;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.fyz.aaronfjava.common.aop.MethodLoggingAspect;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @Author：AaronF
 * @Date：2024/1/24 14:27
 * @description redis服务类 需要在配置文件配置redis相关信息
 */
public class RedisService {

    private static final Logger log = LoggerFactory.getLogger(RedisService.class);

    /**
     * 使用字符串序列化模板
     */
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 对应redis的set操作
     * @param key 键
     * @param value 值
     * @param expireTime 过期时间 单位秒
     */
    public void set(String key, String value, long expireTime) {
        // 校验过期时间是否合法
        if (expireTime <= 0){
            set(key,value);
            return;
        }
        stringRedisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.SECONDS);
        log.info("设置缓存key:{},设置过期时间：{}", key, expireTime);
    }

    /**
     * 对应redis的set操作
     * @param key 键
     * @param value 值
     */
    public void set(String key, String value) {
        stringRedisTemplate.opsForValue().set(key, value);
        log.info("设置缓存key:{},过期时间未操作", key);
    }

    /**
     * 对应redis的setnx操作 当不存在键的时候设置
     * @param key 键
     * @param value 值
     * @param expireTime 过期时间 单位秒
     * @return
     */
    public Boolean setIfAbsent(String key, String value, long expireTime) {
        // 校验过期时间是否合法
        if (expireTime <= 0){
            return setIfAbsent(key,value);
        }
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);
        log.info("设置缓存key:{},设置结果:{}", key, flag);
        return flag;
    }

    public Boolean setIfAbsent(String key, String value) {
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, value);
        log.info("设置缓存key:{},设置结果:{}", key, flag);
        return flag;
    }

    /**
     * 对应redis的get操作
     * @param key 键
     * @return
     */
    public String get(String key) {
        String value = stringRedisTemplate.opsForValue().get(key);
        log.info("缓存key:{}, 缓存数据：{}", key,value);
        return value;
    }

    /**
     * 相当于redis的hset 这里插入的值要求为json字符串
     * @param key 键
     * @param param json字符串
     * @param expireTime 过期时间 单位秒
     */
    public void hashPut(String key, String param,long expireTime) {
        // 校验过期时间是否合法
        if (expireTime <= 0){
            hashPut(key,param);
            return;
        }
        Map<String, String> paramMap = JSONObject.parseObject(param, new TypeReference<Map<String, String>>() {});
        stringRedisTemplate.opsForHash().putAll(key, paramMap);
        stringRedisTemplate.opsForHash().getOperations().expire(key, expireTime, TimeUnit.SECONDS);
        log.info("设置缓存hash key:{},设置过期时间：{}", key,expireTime);
    }

    /**
     * 相当于redis的hset
     * @param key 键
     * @param hashKey 哈希键
     * @param hashValue 值
     * @param expireTime 过期时间 单位秒
     */
    public void hashPut(String key, String hashKey, String hashValue,long expireTime) {
        // 校验过期时间是否合法
        if (expireTime <= 0){
            hashPut(key,hashKey,hashValue);
            return;
        }
        stringRedisTemplate.opsForHash().put(key, hashKey, hashValue);
        stringRedisTemplate.opsForHash().getOperations().expire(key, expireTime, TimeUnit.SECONDS);
        log.info("设置缓存hash key:{},设置过期时间：{}", hashKey,expireTime);
    }

    /**
     * 相当于redis的hset 这里插入的值要求为json字符串
     * @param key 键
     * @param param json字符串
     */
    public void hashPut(String key, String param) {
        Map<String, String> paramMap = JSONObject.parseObject(param, new TypeReference<Map<String, String>>() {});
        stringRedisTemplate.opsForHash().putAll(key, paramMap);
        log.info("设置缓存hash key:{},过期时间未操作", key);
    }

    /**
     * 相当于redis的hset
     * @param key 键
     * @param hashKey 哈希键
     * @param hashValue 值
     */
    public void hashPut(String key, String hashKey, String hashValue) {
        stringRedisTemplate.opsForHash().put(key, hashKey, hashValue);
        log.info("设置缓存hash key:{},过期时间未操作", hashKey);
    }

    /**
     * 相当于redis的hget
     * @param hashKey
     * @return
     */
    public String hashGet(String hashKey) {
        Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(hashKey);
        // 不需要判断空指针
        if (entries.isEmpty()) {
            return null;
        }
        String hashJson = JSONObject.toJSONString(entries);
        log.info("缓存key:{}, 缓存数据：{}", hashKey, hashJson);
        return hashJson;
    }

    /**
     * 尝试对指定的key进行自增操作 相当于redis的INCR操作
     * @param key 键
     * @return 自增后的值
     */
    public Long getAndIncrement(String key) {
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(key))) {
            return stringRedisTemplate.boundValueOps(key).increment(1);
        }
        return null;
    }

    /**
     * redis删除键操作
     * @param key 键
     */
    public Boolean delete(String key) {
        Boolean flag = stringRedisTemplate.delete(key);
        log.info("删除缓存hash key:{},结果:{}", key, flag);
        return flag;
    }

    /**
     * 查看对应键值是否存在于redis
     * @param key 键
     * @return
     */
    public Boolean exist(String key) {
        Boolean flag = stringRedisTemplate.hasKey(key);
        log.info("缓存key:{} 是否存在:{}", key, flag);
        return flag;
    }

    /**
     * 对应redis的ttl操作
     * @param key 键
     * @return 过期时间 单位秒
     */
    public Long ttl(String key) {
        return stringRedisTemplate.opsForValue().getOperations().getExpire(key);
    }

    /**
     * 下面给出一段清除分布式锁lua脚本和代码
     * 其中lua脚本用于保证get和del两个操作原子性
     * 避免通过get获取到锁信息到del间隔中，锁过期，其他线程上锁，导致误删其他线程上的锁
     */

//    private static final String UNLOCK_SCRIPT =
//            "if redis.call('get', KEYS[1]) == ARGV[1] " +
//                    "then " +
//                    "return redis.call('del', KEYS[1]) " +
//                    "else " +
//                    "return 0 end";
//
//    public boolean unlock(String key, String value) {
//        RedisScript<Long> redisScript = new DefaultRedisScript<>(UNLOCK_SCRIPT, Long.class);
//        //返回1表示删除成功
//        Long retCode = stringRedisTemplate.execute(redisScript, Collections.singletonList(key), value);
//        log.info("【redisLock】返回码:{}", retCode);
//        return new Long(1).equals(retCode);
//    }

}
